/*
 * Copyright (c) 2024 Shenzhen Kaihong Digital Industry Development Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <unistd.h>
#include "iservice_registry.h"
#include "i_sle_host.h"
#include "sle_connect_impl.h"
#include "sle_common_config.h"
#include "sle_hilog.h"

namespace OHOS {
namespace NearLink {
namespace SleStandard {
SleConnectManager::SleConnectManagerImpl::SleConnectManagerImpl()
{
    SLE_LOGI("SleAnnounceImpl In");
    callbackImpl_ = new SleConnectCallbackStubImpl(*this);
    SLE_ASSERT_AND_RETURN_LOG(callbackImpl_, "callbackImpl null error!");

    auto resetCallback = [this](const wptr<IRemoteObject> &remote) { this->ResetService(remote); };
    deathRecipient_ = new SleDeathRecipient(resetCallback);
    SLE_ASSERT_AND_RETURN_LOG(deathRecipient_, "deathRecipient null error!");

    std::lock_guard<std::mutex> lock(mutexProxy_);
    GetProxy();
    SLE_ASSERT_AND_RETURN_LOG(proxy_, "get proxy error!");
}

SleConnectManager::SleConnectManagerImpl::~SleConnectManagerImpl()
{
    SLE_LOGI("~SleConnectManagerImpl In");
    SLE_ASSERT_AND_RETURN_LOG(proxy_, "connection proxy null error!");
    SLE_ASSERT_AND_RETURN_LOG(callbackImpl_, "callbackImpl null error!");
    SleErrorCode ret = proxy_->UnregisterConnectCallback(callbackImpl_);
    SLE_ASSERT_AND_RETURN_LOG(ret == SLE_NO_ERROR, "unregister connect callback failed!");
}

void SleConnectManager::SleConnectManagerImpl::GetProxy()
{
    if (proxy_) {
        return;
    }
    sptr<ISystemAbilityManager> samgr = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
    SLE_ASSERT_AND_RETURN_LOG(samgr, "no samgr error!");

    sptr<IRemoteObject> hostRemote = samgr->GetSystemAbility(KH_SLE_SERVICE_SYS_ABILITY_ID_);
    SLE_ASSERT_AND_RETURN_LOG(hostRemote, "no remote error!");
    sptr<ISleHost> hostProxy = iface_cast<ISleHost>(hostRemote);
    SLE_ASSERT_AND_RETURN_LOG(hostProxy, "host proxy null error!");

    sptr<IRemoteObject> remote = hostProxy->GetSleServerRemote(SLE_CONNECT_SERVER);
    SLE_ASSERT_AND_RETURN_LOG(remote, "no remote error!");

    SLE_ASSERT_AND_RETURN_LOG(deathRecipient_, "deathRecipient null error!");
    SLE_ASSERT_AND_RETURN_LOG(!((remote->IsProxyObject()) && (!remote->AddDeathRecipient(deathRecipient_))),
        "Failed to add death recipient");

    proxy_ = iface_cast<ISleConnect>(remote);
    SLE_ASSERT_AND_RETURN_LOG(proxy_, "no proxy error!");
    SleErrorCode ret = proxy_->RegisterConnectCallback(callbackImpl_);
    SLE_ASSERT_AND_RETURN_LOG(ret == SLE_NO_ERROR, "register connect callback failed!");
    SLE_LOGI("GetProxy success");
}

void SleConnectManager::SleConnectManagerImpl::ResetService(const wptr<IRemoteObject> &remote)
{
    SLE_ASSERT_AND_RETURN_LOG(remote != nullptr, "remote null error!");
    std::lock_guard<std::mutex> lock(mutexProxy_);
    SLE_ASSERT_AND_RETURN_LOG(proxy_, "no proxy error!");
    sptr<IRemoteObject> serviceRemote = proxy_->AsObject();
    if ((serviceRemote != nullptr) && (serviceRemote == remote.promote())) {
        serviceRemote->RemoveDeathRecipient(deathRecipient_);
        proxy_ = nullptr;
        int tryTime = 5;
        while ((!proxy_) && tryTime > 0) {
            sleep(GET_PROXY_SLEEP_SEC);
            tryTime--;
            GetProxy();
        }
    }
}

SleErrorCode SleConnectManager::SleConnectManagerImpl::CheckOperation()
{
    std::lock_guard<std::mutex> lock(mutexProxy_);
    GetProxy();
    SLE_ASSERT_AND_RETURN_RET_LOG(proxy_, SLE_ERR_INTERNAL_WRONG, "proxy null error!");
    return SLE_NO_ERROR;
}

SleConnectManager::SleConnectManager()
{
    SLE_LOGD("SleConnectManager In");
    pimpl_ = std::make_unique<SleConnectManagerImpl>();
    SLE_ASSERT_AND_RETURN_LOG(pimpl_, "pimpl null error!");
}

SleConnectManager::~SleConnectManager(){}

SleErrorCode SleConnectManager::Connect(const SleDeviceAddress &address)
{
    SLE_LOGD("Connect In");
    SLE_ASSERT_AND_RETURN_RET_LOG(SleAddress::CheckSleAddress(address) == SLE_NO_ERROR, SLE_ERR_INVALID_PARAM,
    	"param address error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    SleAddress addr(address);
    ret = pimpl_->proxy_->Connect(addr);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "connect failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleConnectManager::Disconnect(const SleDeviceAddress &address)
{
    SLE_LOGD("Disconnect In");
    SLE_ASSERT_AND_RETURN_RET_LOG(SleAddress::CheckSleAddress(address) == SLE_NO_ERROR, SLE_ERR_INVALID_PARAM,
        "param address error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    SleAddress addr(address);
    ret = pimpl_->proxy_->Disconnect(addr);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "disconnect failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleConnectManager::GetConnectId(const SleDeviceAddress &address, uint16_t &connectId)
{
    SLE_LOGD("GetConnectId In");
    SLE_ASSERT_AND_RETURN_RET_LOG(SleAddress::CheckSleAddress(address) == SLE_NO_ERROR, SLE_ERR_INVALID_PARAM,
        "param address error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    SleAddress addr(address);
    ret = pimpl_->proxy_->GetConnectId(addr, connectId);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "get connect id failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleConnectManager::GetConnectState(const SleDeviceAddress &address, SleConnectState &state)
{
    SLE_LOGD("GetConnectState In");
    SLE_ASSERT_AND_RETURN_RET_LOG(SleAddress::CheckSleAddress(address) == SLE_NO_ERROR, SLE_ERR_INVALID_PARAM,
        "param address error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    SleAddress addr(address);
    int32_t connState;
    ret = pimpl_->proxy_->GetConnectState(addr, connState);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "get connect state failed!");
    state = (SleConnectState)connState;
    return SLE_NO_ERROR;
}

SleErrorCode SleConnectManager::GetConnectDeviceList(std::vector<SleConnectDev> &devices, uint16_t &number)
{
    SLE_LOGD("GetConnectDeviceList In");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    std::vector<SleConnectDevice> connDevices;
    ret = pimpl_->proxy_->GetConnectDeviceList(connDevices, number);
    for (auto device : connDevices) {
        SleConnectDev dev;
        device.ConvertToSleConnectDev(dev);
        devices.emplace_back(dev);
    }
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "get connected device list failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleConnectManager::UpdateConnectParam(uint16_t connectId, const SleConnectParam &param)
{
    SLE_LOGD("UpdateConnectParam In");
    SLE_ASSERT_AND_RETURN_RET_LOG(connectId < SLE_MAX_CONNECT_ID_NUM, SLE_ERR_INVALID_PARAM,
		"param connectId error!");
    SleErrorCode ret = SleConnectionParam::CheckConnectParam(param);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check param failed(%{public}d)!", ret);
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    SleConnectionParam params(param);
    ret = pimpl_->proxy_->UpdateConnectParam(connectId, params);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "update connect param failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleConnectManager::UpdateAllConnectParam(std::vector<SleConnectDev> &successUpdateDevList, uint16_t &number)
{
    SLE_LOGD("UpdateAllConnectParam In");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    std::vector<SleConnectDevice> connDevices;
    ret = pimpl_->proxy_->UpdateAllConnectParam(connDevices, number);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "update connect param failed!");
    for (auto device : connDevices) {
        SleConnectDev dev;
        device.ConvertToSleConnectDev(dev);
        successUpdateDevList.emplace_back(dev);
    }
    return SLE_NO_ERROR;
}

SleErrorCode SleConnectManager::GetRecomSendDataSize(uint16_t connectId, uint32_t &recomSendDataSize)
{
    SLE_LOGD("GetRecomSendDataSize In");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = pimpl_->proxy_->GetRecomSendDataSize(connectId, recomSendDataSize);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "update connect param failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleConnectManager::SendPairRequest(const SleDeviceAddress &address)
{
    SLE_LOGD("SendPairRequest In");
    SLE_ASSERT_AND_RETURN_RET_LOG(SleAddress::CheckSleAddress(address) == SLE_NO_ERROR, SLE_ERR_INVALID_PARAM,
        "param address error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    SleAddress addr(address);
    ret = pimpl_->proxy_->SendPairRequest(addr);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "send pairing request failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleConnectManager::RemovePairedDevice(const SleDeviceAddress &address)
{
    SLE_LOGD("RemovePairedDevice In");
    SLE_ASSERT_AND_RETURN_RET_LOG(SleAddress::CheckSleAddress(address) == SLE_NO_ERROR, SLE_ERR_INVALID_PARAM,
        "param address error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    SleAddress addr(address);
    ret = pimpl_->proxy_->RemovePairedDevice(addr);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "remove paired failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleConnectManager::RemoveAllPairedDevices()
{
    SLE_LOGD("RemoveAllPairedDevices In");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = pimpl_->proxy_->RemoveAllPairedDevices();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "remove all paired failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleConnectManager::GetPairedDevicesNum(uint16_t &number)
{
    SLE_LOGD("GetPairedDevicesNum In");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = pimpl_->proxy_->GetPairedDevicesNum(number);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "get paired devices number failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleConnectManager::GetPairedDevices(std::vector<SleDeviceAddress> &devices, uint16_t &number)
{
    SLE_LOGD("GetPairedDevicesNum In");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    std::vector<SleAddress> pairedDevices;
    ret = pimpl_->proxy_->GetPairedDevices(pairedDevices, number);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "get paired devices failed!");
    for (auto device : pairedDevices) {
        devices.emplace_back(device.ConvertToSleDeviceAddress());
    }
    return SLE_NO_ERROR;
}

SleErrorCode SleConnectManager::GetPairState(const SleDeviceAddress &address, SlePairState &state)
{
    SLE_LOGD("GetPairedDevicesNum In");
    SLE_ASSERT_AND_RETURN_RET_LOG(SleAddress::CheckSleAddress(address) == SLE_NO_ERROR, SLE_ERR_INVALID_PARAM,
        "param address error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    SleAddress addr(address);
    uint8_t pairState;
    ret = pimpl_->proxy_->GetPairState(addr, pairState);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "get pairing state failed!");
    state = (SlePairState)pairState;
    return SLE_NO_ERROR;
}

SleErrorCode SleConnectManager::ReadRemoteRssi(uint16_t connectId, int8_t &rssi)
{
    SLE_LOGD("ReadRemoteRssi In");
    SLE_ASSERT_AND_RETURN_RET_LOG(connectId < SLE_MAX_CONNECT_ID_NUM, SLE_ERR_INVALID_PARAM,
		"param connectId error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SleErrorCode ret = pimpl_->CheckOperation();
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, SLE_ERR_CHECKOUT_FAILED, "check operation failed!");
    ret = pimpl_->proxy_->ReadRemoteRssi(connectId);
    SLE_ASSERT_AND_RETURN_RET_LOG(ret == SLE_NO_ERROR, ret, "read remote rssi failed!");

    std::unique_lock<std::mutex> lock(pimpl_->mtx_);
    auto status = pimpl_->conditionVariable_.wait_for(
        lock, std::chrono::milliseconds(3000), [this] { return pimpl_->ready_; });
    pimpl_->ready_ = false;
    if (!status) {
        SLE_LOGE("timeout, read remote rssi failed!");
        return SLE_ERR_TIME_OUT;
    }
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_->errCode_ == SLE_NO_ERROR, pimpl_->errCode_, "read rssi failed!");
    rssi = pimpl_->rssi_;
    return SLE_NO_ERROR;
}

SleErrorCode SleConnectManager::RegisterConnectCallback(std::shared_ptr<SleConnectManagerCallback> callback)
{
    SLE_LOGD("RegisterConnectCallback In");
    SLE_ASSERT_AND_RETURN_RET_LOG(callback, SLE_ERR_INVALID_PARAM, "param callback nullptr error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_->callbacks_.Register(callback),
        SLE_ERR_FAILED, "register connect callback failed!");
    return SLE_NO_ERROR;
}

SleErrorCode SleConnectManager::UnregisterConnectCallback(std::shared_ptr<SleConnectManagerCallback> callback)
{
    SLE_LOGD("UnregisterConnectCallback In");
    SLE_ASSERT_AND_RETURN_RET_LOG(callback, SLE_ERR_INVALID_PARAM, "param callback nullptr error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_, SLE_ERR_INTERNAL_WRONG, "pimpl null error!");
    SLE_ASSERT_AND_RETURN_RET_LOG(pimpl_->callbacks_.Unregister(callback), SLE_ERR_FAILED,
        "unregister connect callback failed!");
    return SLE_NO_ERROR;
}
} // SleStandard
} // NearLink
} // OHOS